#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright (c) 2011, Cédric Krier
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#     * Redistributions of source code must retain the above copyright
#       notice, this list of conditions and the following disclaimer.
#     * Redistributions in binary form must reproduce the above copyright
#       notice, this list of conditions and the following disclaimer in the
#       documentation and/or other materials provided with the distribution.
#     * Neither the name of the <organization> nor the
#       names of its contributors may be used to endorse or promote products
#       derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""Test roundup_cli
"""
from roundup_cli import Roundup, server_required, designator_required
import unittest
from mock import Mock
import ConfigParser
import xmlrpclib


class TestServerRequired(unittest.TestCase):

    def test_server_required(self):

        @server_required
        def foo(self):
            return 'foo'
        mock = Mock()
        mock.roundup_server = None
        self.assertEqual(foo(mock), None)
        mock.roundup_server = Mock()
        self.assertEqual(foo(mock), 'foo')


class TestDesignatorRequired(unittest.TestCase):

    def test_designator_required(self):

        @designator_required
        def foo(self):
            return 'foo'
        mock = Mock()
        mock.designator = None
        self.assertEqual(foo(mock), None)
        mock.designator = True
        self.assertEqual(foo(mock), 'foo')


class TestPrompt(unittest.TestCase):

    def setUp(self):
        self.roundup = Roundup()

    def test_prompt(self):
        "Test prompt"
        self.roundup.section = 'test'
        self.assertEqual(self.roundup.prompt, 'test: ')
        self.roundup.designator = 'issue1'
        self.assertEqual(self.roundup.prompt, 'issue1@test: ')


class TestDoUse(unittest.TestCase):

    def setUp(self):
        self.roundup = Roundup()

    def test_empty_line(self):
        "Test empty line"
        self.roundup.do_use('')
        self.assertEqual(self.roundup.roundup_server, None)

    def test_section(self):
        "Test section"
        self.roundup.config = Mock()
        self.roundup.config.get.return_value = 'test'
        self.roundup.ServerProxy = Mock()
        self.roundup.ServerProxy().schema.return_value = {
            'issue': [['name', '<roundup.hyperdb.String>']]}
        self.roundup.do_use('section')
        self.roundup.ServerProxy.assert_called_with('//test:test@/test',
            allow_none=True)
        self.assertEqual(self.roundup.section, 'section')


class TestCompleteUse(unittest.TestCase):

    def setUp(self):
        self.roundup = Roundup()

    def test_empty_text(self):
        "Test empty text"
        self.roundup.config = Mock()
        self.roundup.config.sections.return_value = ['foo', 'bar']
        self.assertEqual(self.roundup.complete_use('', 'use', 0, 0),
            ['foo', 'bar'])

    def test_partial_value(self):
        "Test partial value"
        self.roundup.config = Mock()
        self.roundup.config.sections.return_value = ['foo', 'bar']
        values = [
            (('f', 'use f', 4, 4), ['foo']),
            (('t', 'use t', 4, 4), []),
            (('foo', 'use foo', 6, 6), ['foo']),
            ]
        for args, result in values:
            self.assertEqual(self.roundup.complete_use(*args), result)

    def test_too_much(self):
        "Test too much values"
        self.assertEqual(self.roundup.complete_use('bar', 'use foo bar',
                10, 10), None)


class TestFormat(unittest.TestCase):

    def setUp(self):
        self.roundup = Roundup()

    def test_not_link(self):
        "Test not link"
        self.roundup.schema = {
            'issue': {
                'name': '<roundup.hyperdb.String>',
                },
            }
        self.assertEqual(self.roundup.format('issue', 'name', 'Test'), 'Test')

    def test_link(self):
        "Test link"
        self.roundup.schema = {
            'issue': {
                'user': '<roundup.hyperdb.Link to "user">',
                },
            }
        self.roundup.display = Mock()
        self.roundup.display.return_value = 'Admin'
        self.assertEqual(self.roundup.format('issue', 'user', 1), 'Admin')
        self.roundup.display.assert_called_with('user', 1, False)

    def test_multilink(self):
        "Test multilink"
        self.roundup.schema = {
            'issue': {
                'messages': '<roundup.hyperdb.Multilink to "msg">',
                },
            }
        self.roundup.display = Mock()
        self.roundup.display.return_value = 'msg'
        self.assertEqual(self.roundup.format('issue', 'messages', [1, 2]),
            'msg,msg')
        self.roundup.display.assert_called_with('msg', 2, False)


class TestDisplay(unittest.TestCase):

    def setUp(self):
        self.roundup = Roundup()
        self.roundup.roundup_server = Mock()

    def test_no_itemid(self):
        "Test no itemid"
        self.roundup.config = Mock()
        self.roundup.config.get.return_value = \
            'username,realname,organisation,address'
        self.assertEqual(self.roundup.display('user', None, multiline=True),
            ['', '', '', ''])
        self.assertEqual(self.roundup.display('user', None, multiline=False),
            '')

    def test_no_option(self):
        "Test no option"
        self.roundup.config = Mock()
        self.roundup.config.get.side_effect = \
            ConfigParser.NoOptionError('test', 'user')
        self.roundup.schema = {
            'user': {
                'username': '<roundup.hyperdb.String>',
                },
            'status': {
                'name': '<roundup.hyperdb.String>',
                },
            'message': {
                'content': '<roundup.hyperdb.String>',
                },
            }
        self.roundup.roundup_server.display = Mock()
        self.roundup.roundup_server.display.return_value = {
            'username': 'Admin',
            'realname': 'Administrator',
            }
        self.roundup.format = Mock()
        self.roundup.format.return_value = 'Test'
        self.assertEqual(self.roundup.display('user', '1'),
            ['realname: Test', 'username: Test'])
        self.assertEqual(self.roundup.display('user', '1', multiline=False),
            'Test')
        self.roundup.format.return_value = 'Foo\nBar'
        self.assertEqual(self.roundup.display('user', '1'),
            ['realname:', 'Foo\nBar', 'username:', 'Foo\nBar'])

        self.roundup.roundup_server.display.return_value = {
            'name': 'unread',
            }
        self.roundup.format.return_value = 'unread'
        self.assertEqual(self.roundup.display('status', '1', multiline=False),
            'unread')

        self.roundup.roundup_server.display.return_value = {
            'content': 'Foo\nBar',
            }
        self.roundup.format.return_value = 'Foo\nBar'
        self.assertEqual(self.roundup.display('message', '1', multiline=False),
            'Foo\nBar')

    def test_option(self):
        "Test with option"
        self.roundup.config = Mock()
        self.roundup.config.get.return_value = \
            'username,realname,organisation,address'
        self.roundup.schema = {
            'user': {
                'username': '<roundup.hyperdb.String>',
                },
            }
        self.roundup.roundup_server.display = Mock()
        self.roundup.roundup_server.display.return_value = {
            'username': 'Admin',
            'realname': 'Administrator',
            'organisation': '',
            'address': '',
            }
        self.roundup.format = Mock()
        self.roundup.format.return_value = 'Test'
        self.assertEqual(self.roundup.display('user', '1'),
            ['username: Test', 'realname: Test',
                'organisation: Test', 'address: Test'])


class TestSplitDesignator(unittest.TestCase):

    def setUp(self):
        self.roundup = Roundup()

    def test_match(self):
        "Test match"
        self.assertEqual(self.roundup.split_designator('issue1'),
            ('issue', '1'))

    def test_not_match(self):
        "Test not match"
        self.assertEqual(self.roundup.split_designator(''),
            (None, None))
        self.assertEqual(self.roundup.split_designator('issue'),
            (None, None))
        self.assertEqual(self.roundup.split_designator('1issue'),
            (None, None))


class TestGet(unittest.TestCase):

    def setUp(self):
        self.roundup = Roundup()

    def test_no_line(self):
        "Test no line"
        self.assertEqual(self.roundup.do_get(''), None)
        self.assertEqual(self.roundup.designator, None)

    def test_designator(self):
        "Test with designator"
        self.roundup.display = Mock()
        self.roundup.display.return_value = 'display'
        self.roundup.print_ = Mock()
        self.roundup.do_get('issue1')
        self.assertEqual(self.roundup.designator, 'issue1')
        self.roundup.display.assert_called_with('issue', '1')
        self.roundup.print_.assert_called_with('display')

    def test_xmlrpc_fault(self):
        "Test with xmlrpc fault"
        self.roundup.display = Mock()
        self.roundup.display.side_effect = xmlrpclib.Fault(1, 'Error')
        self.roundup.do_get('issue1')
        self.assertEqual(self.roundup.designator, None)


class TestCompleteGet(unittest.TestCase):

    def setUp(self):
        self.roundup = Roundup()

    def test_empty_text(self):
        "Test empty text"
        self.roundup.schema = Mock()
        self.roundup.schema.keys.return_value = ['issue', 'user']
        self.assertEqual(self.roundup.complete_get('', 'get', 0, 0),
            ['issue', 'user'])

    def test_partial_value(self):
        "Test partial value"
        self.roundup.schema = Mock()
        self.roundup.schema.iterkeys.return_value = ['issue', 'user']
        values = [
            (('i', 'get i', 4, 4), ['issue']),
            (('t', 'get t', 4, 4), []),
            (('issue', 'get issue', 8, 8), ['issue']),
            ]
        for args, result in values:
            self.assertEqual(self.roundup.complete_get(*args), result)

    def test_too_much(self):
        "Test too much values"
        self.assertEqual(self.roundup.complete_get('user',
                'get issue user', 13, 13), None)


class TestDoShow(unittest.TestCase):

    def setUp(self):
        self.roundup = Roundup()
        self.roundup.designator = Mock()
        self.roundup.split_designator = Mock()
        self.roundup.split_designator.return_value = ['issue', None]
        self.roundup.schema = {
            'issue': {
                'name': '<roundup.hyperdb.String>',
                'user': '<roundup.hyperdb.Link to "user">',
                'messages': '<roundup.hyperdb.Multilink to "msg">',
                }}
        self.roundup.roundup_server = Mock()
        self.roundup.roundup_server.display.return_value = {
            'name': 'Test',
            'user': 1,
            'messages': [1, 2],
            }
        self.roundup.display = Mock()
        self.roundup.display.return_value = ['display']
        self.roundup.print_ = Mock()

    def test_empty_line(self):
        "Test empty line"
        self.assertEqual(self.roundup.do_show(''), None)
        self.roundup.roundup_server.display.return_value = {}
        self.roundup.do_show('')
        self.roundup.print_.assert_called_with([
                'display',
                ])

    def test_properties(self):
        "Test with properties"
        self.roundup.do_show('name user messages')
        self.roundup.print_.assert_called_with([
                'name: Test',
                'user1',
                'display',
                'messages:',
                '-- ',
                'msg1',
                'display',
                '-- ',
                'msg2',
                'display',
                '-- ',
                ])

    def test_wrong_property(self):
        "Test with wrong property"
        self.roundup.do_show('foo')


class TestCompleteShow(unittest.TestCase):

    def setUp(self):
        self.roundup = Roundup()
        self.roundup.designator = Mock()
        self.roundup.split_designator = Mock()
        self.roundup.split_designator.return_value = ['issue', None]
        self.roundup.schema = {
            'issue': {
                'name': '<roundup.hyperdb.String>',
                'user': '<roundup.hyperdb.Link to "user">',
                'messages': '<roundup.hyperdb.Multilink to "msg">',
                }}

    def test_empty_text(self):
        "Test empty text"
        self.assertEqual(self.roundup.complete_show('', 'show', 0, 0),
            ['messages', 'name', 'user'])

    def test_partial_value(self):
        "Test partial value"
        values = [
            (('n', 'show n', 5, 5), ['name']),
            (('t', 'show t', 5, 5), []),
            (('name', 'show name', 8, 8), ['name']),
            ]
        for args, result in values:
            self.assertEqual(self.roundup.complete_show(*args), result)


class TestGetLinks(unittest.TestCase):

    def setUp(self):
        self.roundup = Roundup()
        self.roundup.roundup_server = Mock()
        self.roundup.roundup_server.filter.return_value = [1, 2]
        self.roundup.display = Mock()
        self.roundup.display.return_value = 'display'

    def test_link(self):
        "Test link"
        self.assertEqual(self.roundup.get_links('user'), {
                1: 'display',
                2: 'display',
                })


class TestDoSearch(unittest.TestCase):

    def setUp(self):
        self.roundup = Roundup()
        self.roundup.designator = Mock()
        self.roundup.split_designator = Mock()
        self.roundup.split_designator.return_value = ['issue', None]
        self.roundup.schema = {
            'issue': {
                'name': '<roundup.hyperdb.String>',
                'user': '<roundup.hyperdb.Link to "user">',
                'messages': '<roundup.hyperdb.Multilink to "msg">',
                }}
        self.roundup.roundup_server = Mock()
        self.roundup.roundup_server.filter.return_value = [1]
        self.roundup.display = Mock()
        self.roundup.display.return_value = 'display'
        self.roundup.print_ = Mock()
        self.roundup.get_links = Mock()
        self.roundup.get_links.return_value = {1: 'bar'}

    def test_empty_line(self):
        "Test empty line"
        self.roundup.do_search('')

    def test_classname(self):
        "Test with classname"
        self.roundup.do_search('issue')
        self.roundup.roundup_server.filter.assert_called_with('issue', None,
            {})
        self.roundup.print_.assert_called_with([
                'issue1: display',
                ])

    def test_wrong_classname(self):
        "Test wrong classname"
        self.roundup.do_search('foo')
        self.roundup.do_search('foo ')

    def test_properties(self):
        "Test with properties"
        self.roundup.do_search('issue name:foo')
        self.roundup.roundup_server.filter.assert_called_with('issue', None,
            {'name': 'foo'})
        self.roundup.do_search('issue name: foo')
        self.roundup.roundup_server.filter.assert_called_with('issue', None,
            {'name': 'foo'})
        self.roundup.do_search('issue name:foo,user:bar')
        self.roundup.roundup_server.filter.assert_called_with('issue', None,
            {'name': 'foo', 'user': 1})
        self.roundup.do_search('issue name: foo, user: bar')
        self.roundup.roundup_server.filter.assert_called_with('issue', None,
            {'name': 'foo', 'user': 1})

    def test_wrong_properties(self):
        "Test wrong properties"
        self.roundup.do_search('issue name,user')
        self.roundup.roundup_server.filter.assert_called_with('issue', None,
            {})
        self.roundup.do_search('issue foo:bar')
        self.roundup.roundup_server.filter.assert_called_with('issue', None,
            {})
        self.roundup.do_search('issue user: foo')
        self.roundup.roundup_server.filter.assert_called_with('issue', None,
            {})


class TestCompleteSearch(unittest.TestCase):

    def setUp(self):
        self.roundup = Roundup()
        self.roundup.roundup_server = Mock()
        self.roundup.schema = {
            'issue': {
                'name': '<roundup.hyperdb.String>',
                'user': '<roundup.hyperdb.Link to "user">',
                'messages': '<roundup.hyperdb.Multilink to "msg">',
                }}
        self.roundup.get_links = Mock()
        self.roundup.get_links.return_value = {1: 'bar'}

    def test_empty_text(self):
        "Test empty text"
        self.assertEqual(self.roundup.complete_search('', 'search', 0, 0),
            ['issue'])

    def test_partial_value(self):
        "Test partial value"
        self.assertEqual(self.roundup.complete_search('is', 'search is', 8, 8),
            ['issue'])
        self.assertEqual(self.roundup.complete_search(
                'foo', 'search foo', 8, 8), [])

    def test_properties(self):
        "Test properities"
        self.assertEqual(sorted(self.roundup.complete_search(
                    '', 'search issue ', 12, 12)),
            sorted(['name:', 'user:', 'messages:']))
        self.assertEqual(self.roundup.complete_search(
                'n', 'search issue n', 13, 13), ['name:'])
        self.assertEqual(self.roundup.complete_search(
                '', 'search issue name: ', 18, 18), None)
        self.assertEqual(self.roundup.complete_search(
                'foo', 'search issue name: foo', 21, 21), None)
        self.assertEqual(self.roundup.complete_search(
                'u', 'search issue u', 13, 13), ['user:'])
        self.assertEqual(self.roundup.complete_search(
                '', 'search issue user: ', 18, 18), ['bar,'])
        self.assertEqual(self.roundup.complete_search(
                'b', 'search issue user: b', 19, 19), ['bar,'])

    def test_wrong_properties(self):
        "Test wrong properities"
        self.assertEqual(self.roundup.complete_search(
                '', 'search issue foo:', 16, 16), None)


class TestEmptyline(unittest.TestCase):

    def setUp(self):
        self.roundup = Roundup()

    def test_emptyline(self):
        self.assertEqual(self.roundup.emptyline(), None)


class TestDoEOF(unittest.TestCase):

    def setUp(self):
        self.roundup = Roundup()
        self.roundup._print = Mock()

    def test_do_EOF(self):
        self.assert_(self.roundup.do_EOF(''))
        self.roundup._print.assert_called_with()


class TestPrint_(unittest.TestCase):

    def setUp(self):
        self.roundup = Roundup()
        self.roundup._print = Mock()
        self.roundup.pager = Mock()

    def test_print_(self):
        self.roundup.print_(['foo'], _width=79)
        self.roundup._print.assert_called_with(' foo')
        self.roundup.print_(['issue1', '-- ', 'display\n'], _width=79)
        self.roundup._print.assert_called_with(
            ' issue1\n'
            + ('-' * 79) + '\n'
            '  display')
        self.roundup.print_(['a'] * 20, 10)
        self.roundup.pager.assert_called_with((' a\n' * 20)[:-1])

if __name__ == '__main__':
    unittest.main()
